<?php
/**
 * Game_Place
 *
 * Класс Game_Place обрабатывает данные, связанные с местами (деревнями)
 *
 * @author Дмитрий Кузьма <kouzma.d@gmail.com>
 * @version 1.1
 */
class Game_Place extends ActiveRecord {
	protected static $_tableName = 'place';
	protected static $_pkName = 'ID_place';
	
	
	public $objResourcesWithProduction;// Game_ResourceWithProduction;
	public $objCoordinates;// Game_Coordinates;
	protected $_culture; // Культура в месте
	protected $_cultureProduction; // Производство культуры (в сутки)
	protected $_lastCalc; // Производство культуры (в сутки)
	protected $_arrBuildings; // Массив зданий, остроенных в соответсвующем месте
	
	protected $_arrUnitsStatuses; // Массив статусов юнитов в соответсвующем месте
	protected $_arrEvents; // Массив событий в соответсвующем месте
	protected $_arrOwnUnits; // Массив юнитов в соответсвующем месте
	protected $_arrOwnUnitsInMove; // Массив юнитов, находящихся в движении
	protected $_arrGuestUnits; // Массив юнитов в соответсвующем месте
	
		/**
	 * Конструктор объекта (создание, инициализация, загрузка данных) 
	 * 
	 * @param mixed $arrInitialFields - ID объекта, либо массив с данными, либо строка что ищем
	 * @param array $arrSettings - Массив с настройками
	 */
	public function __construct($arrInitialFields = null, array $arrSettings = array()) {
		parent::__construct($arrInitialFields, $arrSettings);
		if (is_numeric($arrInitialFields)) {
			$this->objResourcesWithProduction = new Game_ResourceWithProduction($this->tableFields);
			$this->objCoordinates = new Game_Coordinates($this->tableFields);
			$this->_cultureProduction = $this->getField('culture_production');
			$this->_culture = $this->getField('culture');
			$this->_lastCalc = $this->getField('last_calc');
		}
	}
	
	
	/**
	 * Обновляет данные о строениях в данном месте 
	 */
	public function refreshBuildingsInfo() {
		$this->_getBuildings();
	}
	
	/**
	 * Заполняет список отсроенных зданий в месте
	 * 
	 */
	protected function _getBuildings() {
		if (!$this->id) return array();
		$stmt = " SELECT * FROM `place_building`
			INNER JOIN `building` USING(`ID_building`)
			WHERE `place_building`.`ID_place` = " . $this->id . "
		";
		$objDB = Service_Database::instance();
		$this->_arrBuildings = $objDB->select($stmt);
	}
	
	/**
	 * Получает список, отсроенных в месте зданий
	 * 
	 * @return array Возвращает массив отстроенных зданий
	 */
	public function getBuildingsData() {
		if (!isset($this->_arrBuildings)) $this->_getBuildings();
		return $this->_arrBuildings;
	}
	
	/**
	 * Возвращает информацию о построенном в месте здании по его ID
	 * Метод может работать некорректно, если предполагается строительство нескольких зданий
	 * 
	 * @param int $buildingId
	 * 
	 * @return array
	 */
	public function getBuildingInfo($buildingId) {
		$this->_getBuildings();
		foreach ($this->_arrBuildings as $arrBuilding) {
			if ($arrBuilding['ID_building'] == $buildingId) {
				return $arrBuilding;
			}
		}
		return array();
	}
	
	/**
	 * Получает ID-здания по его позиции
	 * 
	 * @param int $positionId - номер позиции здания
	 */
	public function getBuildingIdByPosition($positionId) {
		$this->getBuildingsData();
		foreach ($this->_arrBuildings as $arrBuilding) {
			if ($arrBuilding['ID_position'] == $positionId) return $arrBuilding['ID_building'];
		}
		return null;
	} 
	
	/**
	 * Сохраняет текущие значения ресурсам 
	 * 
	 */
	public function saveResourcesData() {
		$this->setFields($this->objResourcesWithProduction->getDataForDatabase());
		$this->setField('culture_production', $this->_cultureProduction);
		$this->setField('culture', $this->_culture);
		$this->setField('last_calc', Service_Date::time2BD());
		$this->save();
	}

	/**
	 * Определяет текущее производство в месте
	 *
	 */
	private function _calculateProduction() {
		$this->getBuildingsData();
		$this->objResourcesWithProduction->calculateProduction($this->_arrBuildings);
		$this->objResourcesWithProduction->calculateConsumptionStaticData($this->_arrBuildings);
	}	
	
	
	/**
	 * Определяет текущую вместимость в месте
	 * 
	 */
	private function _calculateStorage() {
		$this->getBuildingsData();
		$this->objResourcesWithProduction->calculateCapacity($this->_arrBuildings);
	}
	
	/**
	 * Определяет текущее производство культуры в месте
	 *
	 */
	private function _calculateCultureProduction() {
		$this->getBuildingsData();
		$cultureProduction  = 0;
		foreach ($this->_arrBuildings as $arrBuilding) {
			$cultureProduction += Game_Building_Item::getBuldingCultureProduction($arrBuilding);
		}
		$this->_cultureProduction = $cultureProduction;
	}
	
	/**
	 * Определяет текущую культуру в месте
	 *
	 */
	private function _calculateCulture() {
		$timeDiff = Service_Date::timePassed($this->_lastCalc);
		$secondsAtDay = Service_Date::$timestampOneDayInterval;
		$this->_culture += $this->_cultureProduction * $timeDiff / $secondsAtDay;
	}
	
	/**
	 * Получает скорость строительства здания. В процентах. От 96% и выше.
	 */
	function getBuildingSpeed() {
		$arrTimeBuildingInfo = $this->_getBuildingById(5);
		return Game_Building_Main::getBuildingSpeed($arrTimeBuildingInfo['level']);
	}

	/**
	 * Получает двумерный массив зданий согласно идентификатору здания
	 *
	 * @param int $buildingId
	 * @return array
	 */
	function _getBuildingsById($buildingId) {
		$this->_getBuildings();
		$arrFoundBuildings = array();
		foreach ($this->_arrBuildings as $arrBuilding) {
			if ($arrBuilding['ID_building'] == $buildingId) {
				$arrFoundBuildings[] = $arrBuilding;
			}
		}
		return $arrFoundBuildings;
	}

	/**
	 * Получает одномерный  массив с информацией о зданий согласно идентификатору здания
	 *
	 * @param int $buildingId
	 * @return array
	 */
	function _getBuildingById($buildingId) {
		$this->_getBuildings();
		foreach ($this->_arrBuildings as $arrBuilding) {
			if ($arrBuilding['ID_building'] == $buildingId) {
				return $arrBuilding;
			}
		}
	}
	
	
	/**
	 * Подсчитывает и обновляет основные параметры места
	 */
	public function calculateAndUpdatePlaceParams() {
		$this->_calculateProduction(); // Подсчитываем свежие данные по ресурсам и производству
		$this->_calculateStorage();
		$this->_calculateCulture();
		$this->_calculateCultureProduction();
		$this->saveResourcesData(); // Сохраняем текущее производство
	}
	/**
	 * Подсчитывает и обновляет динамическую информацию о текущем месте
	 */
	public function calculateAndUpdateDynamicConsumption() {
		$this->_calculateDynamicConsumption(); // Подсчитываем свежие данные по ресурсам и производству
		//$this->objResourcesWithProduction->calculateProduction($this->_arrBuildings);
		//$this->objResourcesWithProduction->calculateConsumptionStaticData($this->_arrBuildings);
		$this->saveResourcesData(); // Сохраняем текущее производство
	}
	
	/**
	 * Вычисляет текущее потребление не статическими объектами 
	 */
	private function _calculateDynamicConsumption() {
		$arrUnits = $this->getUnitsConnectedToPlace();
		$this->objResourcesWithProduction->calculateConsumptionDynamicData($arrUnits);
	}
	
	
	
	/**
	 * Получает список зданий, которые могут быть построены в этом месте
	 *
	 * @return array:
	 */
	public function getBuildingsToBeCreate() {
		$arrRet = array();
		$arrAllBuildings = Game_Building::getAllBuildingsInfo();
		$arrPlaceBuildings = $this->getBuildingsData();
		$arrBuildingsDependences = Game_Building::getBuildingsDependencesByIds(Service_ArrayConverter::getColumn($arrAllBuildings, 'ID_building'));
		foreach ($arrAllBuildings as $arrBuilding) {
			if ($arrBuilding['ID_building'] < 6) continue;		// TODO не универсальная проверка при изменении числа ресурсов. По хорошему стоит переписать
			$objCurrentBuilding = new Game_Building($arrBuilding);
			// Если возможно построить здание
			if ($objCurrentBuilding->isAvailabbleToBuildInPlaceAnyTime($arrPlaceBuildings)) {
				$arrBuilding['unperformed_dependences'] = $objCurrentBuilding->getNecessaryBuildingsToBuildInPlace($arrPlaceBuildings, $arrBuildingsDependences);
				$arrRet[] = $arrBuilding; 
			}
		}
	
		return $arrRet;
	}
	
	/**
	 * Получает информацию о статусах юнитов в текущем месте
	 * 
	 * @return Ambigous <multitype:, Game_Unit_Status>
	 */
	public function getUnitsStatuses() {
		if (!isset($this->_arrUnitsStatuses)) $this->_arrUnitsStatuses = Game_Unit_Status::getPlaceUnitsStatuses(true);
		return $this->_arrUnitsStatuses;
	}
	
	/**
	 * Получает информацию о событиях в текущем месте
	 *
	 * @return Ambigous <multitype:, Game_Unit_Status>
	 */
	public function getEvents() {
		if (!isset($this->_arrEvents)) {
			$this->_fillEvents();
		}
		return $this->_arrEvents;
	}
	
	/**
	 * Заполняет события в данном месте
	 */
	private function _fillEvents() {
		$this->_arrEvents = array(
			'building' => array(),
			'research' => array()
		);
		
		// Получаем события
		$stmt = "
			SELECT `game_event`.*
			FROM `game_event`
			WHERE `game_event`.`ID_place` = '" . $this->id . "'
		";
		$objDB = Service_Database::instance();
		$arrEvents = $objDB->select($stmt);
		
		// Обрабатываем все полученные события
		foreach ($arrEvents as $arrEvent) {
			$objEventItem = Game_Event_ItemCreator::initBuildingItem(new Game_Event($arrEvent));
			$eventType = $objEventItem->getEventType();
			$essenceId = $arrEvent['ID_essence'];
			$this->_arrEvents[$eventType][$essenceId] = $objEventItem;
		}
	}
	
	/**
	 * Получает события со зданиями в данном месте
	 * 
	 * @return array - массив, сгруппированный по идентификаторам с объектами событий постройки зданий
	 */
	public function getBuildingsEvents() {
		$this->getEvents();
		return $this->_arrEvents['building'];
	}
	
	/**
	 * Получает события с исследованиями в данном месте
	 * 
	 * @return array - массив, сгруппированный по идентификаторам с объектами событий исследования юнитов
	 */
	public function getResearchsEvents() {
		$this->getEvents();
		return $this->_arrEvents['research'];
	}

	/**
	 * Проверяет, имеются ли исследовательские задачи в данном месте
	 *
	 * return bool
	 */
	public function hasResearchEvents() {
		return sizeof($this->getResearchsEvents());
	}
	

	/**
	 * Проверяет, имеются ли задачи строительства в данном месте
	 *
	 * return bool
	 */
	public function hasBuildingEvents() {
		return sizeof($this->getBuildingsEvents());
	}
	
	/**
	 * Подсчитывает количество потребляемых динамических ресурсов 
	 * 
	 * @return array
	 */
	public function calculateDynamicConsumption() {
		
	} 
	
		
	
	/**
	 * Получает информацию о юнитах в текущем месте, которых можно отправить в путь
	 *
	 * @return Ambigous <multitype:, Game_Unit_Status>
	 */
	public function getUnitsAvailableToFight() {
		if (!isset($this->_arrOwnUnits)) $this->_fillUnits();
		if (!isset($this->_arrOwnUnitsInMove)) $this->_fillOwnUnitsInMove();
		$arrRet = array();
		foreach ($this->_arrOwnUnits as $unitId => $ownAmount) {
			$amountAvailableToFight = $ownAmount - (isset($this->_arrOwnUnitsInMove[$unitId]) ? $this->_arrOwnUnitsInMove[$unitId] : 0);
			if ($amountAvailableToFight) $arrRet[$unitId] = $amountAvailableToFight;
		}
		return $arrRet;
	}
	
	/**
	 * Получает информацию о юнитах, которые реально находятся в текущем месте
	 *
	 * @return Ambigous <multitype:, Game_Unit_Status>
	 */
	public function getUnitsRealInPlace() {
		if (!isset($this->_arrOwnUnits) || !isset($this->_arrGuestUnits)) $this->_fillUnits();
		if (!isset($this->_arrOwnUnitsInMove)) $this->_fillOwnUnitsInMove();
		$arrRet = array();
		$arrCheckedGuestUnitIds = array();
		foreach ($this->_arrOwnUnits as $unitId => $ownAmount) {
			$amountAvailableToFight = $ownAmount - (isset($this->_arrOwnUnitsInMove[$unitId]) ? $this->_arrOwnUnitsInMove[$unitId] : 0);
			if ($amountAvailableToFight) $arrRet[$unitId] = $amountAvailableToFight;
		}
		foreach ($this->_arrGuestUnits as $unitId => $guestAmount) {
			$arrRet[$unitId] = (isset($arrRet[$unitId]) ? $arrRet[$unitId] += $guestAmount : $guestAmount);
		}
		return $arrRet;
	}
	
	/**
	 * Получает информацию о торговцах в текущем месте, которых можно отправить в путь
	 *
	 * @return Ambigous <multitype:, Game_Unit_Status>
	 */
	public function getTradersAvailableToGo() {
		$arrMarket = $this->getBuildingInfo(8);
		$marketLevel = (isset($arrMarket['level']) ? $arrMarket['level'] : 0);
		$stmt = "
				SELECT SUM(`amount`) 
				FROM `units_group` 
				WHERE `ID_place_owner` = '" . $this->id . "' AND `move_type` = 'trade'
		";
		$objDB = Service_Database::instance();
		$tradersInWay =  $objDB->selectSimpleField($stmt);
		return $marketLevel - $tradersInWay;
		/*
		if (!isset($this->_arrOwnUnits)) $this->_fillUnits();
		if (!isset($this->_arrOwnUnitsInMove)) $this->_fillOwnUnitsInMove();
		$arrRet = array();
		foreach ($this->_arrOwnUnits as $unitId => $ownAmount) {
			$amountAvailableToFight = $ownAmount - (isset($this->_arrOwnUnitsInMove[$unitId]) ? $this->_arrOwnUnitsInMove[$unitId] : 0);
			if ($amountAvailableToFight) $arrRet[$unitId] = $amountAvailableToFight;
		}
		return $arrRet;
		*/
	}
	
	/**
	 * Заполняет информацию о юнитах в данном месте
	 */
	private function _fillUnits() {
		// Получаем юнитов
		$arrUnits = $this->getUnitsConnectedToPlace();
		$arrOwnUnits = array();
		$arrGuestUnits = array();
		foreach ($arrUnits as $arrUnit) {
			$unitId = $arrUnit['ID_unit'];
			$arrToSave = &$arrOwnUnits;
			if ($arrUnit['ID_place'] != $arrUnit['ID_place_owner']) $arrToSave = &$arrGuestUnits;
			if (!isset($arrToSave[$unitId])) $arrToSave[$unitId] = $arrUnit['amount'];
			else $arrToSave[$unitId] += $arrUnit['amount'];
		}
		$this->_arrOwnUnits = $arrOwnUnits;
		$this->_arrGuestUnits = $arrGuestUnits;
	}
	

	/**
	 * Заполняет информацию о юнитах в данном месте
	 */
	private function _fillOwnUnitsInMove() {
		$arrUnitsInMove = $this->_getOwnUnitsInMove();
		$this->_arrOwnUnitsInMove = array();
		foreach ($arrUnitsInMove as $arrUnit) {
			$unitId = $arrUnit['ID_unit'];
			if (!isset($this->_arrOwnUnitsInMove[$unitId])) $this->_arrOwnUnitsInMove[$unitId] = $arrUnit['amount'];
			else $this->_arrOwnUnitsInMove[$unitId] += $arrUnit['amount'];
		}
	}
	
	/**
	 * Получает список юнитов, относящихся к текущему месту
	 * 
	 * @return Ambigous <multitype:, multitype:multitype: >
	 */
	public function getUnitsConnectedToPlace() {
		$stmt = "
			SELECT `place_unit_amount`.*
			FROM `place_unit_amount`
			WHERE `place_unit_amount`.`ID_place` = '" . $this->id . "'
		";
		$objDB = Service_Database::instance();
		return $objDB->select($stmt);
	}
	

	/**
	 * Получает список собственных юнитов в пути
	 * 
	 * @return Ambigous <multitype:, multitype:multitype: >
	 */
	private function _getOwnUnitsInMove() {
		$stmt = "
			SELECT `units_in_group`.*
			FROM `game_event`
			INNER JOIN `units_in_group` ON `units_in_group`.`ID_units_group` = `game_event`.`ID_essence`
			WHERE `game_event`.`ID_place` = '" . $this->id . "' AND `game_event`.`event_type` = 'units_move'
		";
		$objDB = Service_Database::instance();
		return $objDB->select($stmt);		
	}
	
	/**
	 * Получает объект пользователя
	 *
	 */
	public function getUser() {
		return new Game_User($this->getField('ID_user'));
	}
	
	/**
	 * Получает ресурсы, которые могут быть украдены
	 * 
	 * @return Game_Resource
	 */
	public function getResourcesCouldBeStolen() {
		return new Game_Resource($this->objResourcesWithProduction->getResourcesData());//->getResourcesData()
	}

}
?>